//////////////////////////////////////////////////////////////////////
//
// NO_Object.cpp interface for the NO_Object class.
//
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
#define NO_TAL_EXPORT

//////////////////////////////////////////////////////////////////////////
#include "NO_Externs.h"
#include "NO_Object.h"

//////////////////////////////////////////////////////////////////////////
// class NO_EmptyObject definition
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
NO_EmptyObject				NO_EmptyObject::empty;
NO_Object::ObjectDelegates	NO_Object::onConstruction; 
NO_Object::ObjectDelegates	NO_Object::onDestruction; 

//////////////////////////////////////////////////////////////////////
// con/destruction
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
NO_EmptyObject::NO_EmptyObject(void) {}

//////////////////////////////////////////////////////////////////////
// retrieve
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
NO_UInt32		NO_EmptyObject::getID(void)			 const {return 0;}
NO_UInt32		NO_EmptyObject::getNum(void)		 const {return 0;}
std::string		NO_EmptyObject::getName(void)		 const {return "EmptyObject";}
const NO_Type&	NO_EmptyObject::getType(void)		 const {return NO_TypeID<NO_Object>::type();}
const NO_Type&	NO_EmptyObject::getDynamicType(void) const {return NO_TypeID<NO_Object>::type();}
NO_Boolean		NO_EmptyObject::getInitialized(void) const {return 0;}

//////////////////////////////////////////////////////////////////////////
// class NO_ObjectIter
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
NO_ObjectIter::NO_ObjectIter (NO_Value::ObjectSetIter& iter):iter(iter){}

//////////////////////////////////////////////////////////////////////////
bool		NO_ObjectIter::operator!=	(const NO_ObjectIter& rhs) const {return iter != rhs.iter;}
bool		NO_ObjectIter::operator==	(const NO_ObjectIter& rhs) const {return iter == rhs.iter;}
NO_Object	NO_ObjectIter::operator*	(void)							 {return &*iter;}
NO_ObjectIter NO_ObjectIter::operator++	(int)							 {return iter++;}
NO_ObjectIter NO_ObjectIter::operator++	(void)							 {return ++iter;}

//////////////////////////////////////////////////////////////////////////
// class NO_Object
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// con/destruction
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
NO_Object& NO_Object::empty = NO_EmptyObject::empty;

//////////////////////////////////////////////////////////////////////////
NO_Object::NO_Object(void):data(0)			{}
NO_Object::NO_Object(Data* rhs):data(rhs)	{if(data){++(data->numRef);}}

//////////////////////////////////////////////////////////////////////
NO_Object::NO_Object(const NO_Object& rhs)								:data(0) {assign(rhs);}
NO_Object::NO_Object(const NO_Type& i,NO_UInt32 num)					:data(0) {initialize(i,0,num);}
NO_Object::NO_Object(const NO_Int8* t, NO_UInt32 num)					:data(0) {initialize(NO_TypeID<>::type(t),0,num);}
NO_Object::NO_Object(const NO_Type& t, const NO_Int8* n, NO_UInt32 num)	:data(0) {initialize(t,n,num);}
NO_Object::NO_Object(const NO_Int8* t, const NO_Int8* n, NO_UInt32 num)	:data(0) {initialize(NO_TypeID<>::type(t),n,num);}

//////////////////////////////////////////////////////////////////////
NO_Object::~NO_Object()
{
	if(!data){return;}
	if((--(data->numRef))==0){release();}
}

//////////////////////////////////////////////////////////////////////
// operators
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
			NO_Object::operator NO_Boolean			(void)	const {if(data==0){return 0;} return (data->hasData());}
NO_Boolean	NO_Object::operator !					(void)	const {if(data==0){return 1;} return (!data->hasData());}
NO_Boolean	NO_Object::operator ==	(const NO_Object& rhs)	const {return data==rhs.data;}
NO_Boolean	NO_Object::operator !=	(const NO_Object& rhs)	const {return data!=rhs.data;}
NO_Object&	NO_Object::operator =	(const NO_Object& rhs)		  {assign(rhs);	return *this;}

//////////////////////////////////////////////////////////////////////////
// modify
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
NO_Object& NO_Object::assign(const NO_Object& rhs)
{
	
	if(&rhs==this)		{return *this;}
	if(data==rhs.data)	{return *this;}
	
	if(data){if((--(data->numRef))==0){release();}}
	data = rhs.data;
	if(data){++(data->numRef);}

	return *this;
}

//////////////////////////////////////////////////////////////////////
NO_Object& NO_Object::assign(Data* rhs)	
{
	
	if(data==rhs)	{return *this;}

	if(data){if((--(data->numRef))==0){release();}}
	data = rhs;
	if(data){++(data->numRef);}
	return *this;
}

//////////////////////////////////////////////////////////////////////////
void NO_Object::initialize(const NO_Type& t, const NO_Int8* n, NO_UInt32 i)
{
	

	if(data){if((--(data->numRef))==0){release();}}
	const NO_IConstructor* con = t.findConstructor(0);
	
	if(!con) {throw std::exception("Can not find standard constructor.");}
	data = con->construct(1,NO_ObjectList::empty);

	if(data)
	{
		if(n){data->setName(n);}
		(++(data->numRef));

		ObjectDelegateIter iter = onConstruction.begin();
		ObjectDelegateIter end = onConstruction.end();
		while (iter != end){if (*iter){(**iter)(*this);}++iter;}
	}
}

//////////////////////////////////////////////////////////////////////////
void NO_Object::release(void)
{
	if (data->getIsConstructed())
	{
		ObjectDelegateIter iter = onDestruction.begin();
		ObjectDelegateIter end = onDestruction.end();
		while (iter != end)
		{
			if (*iter){(**iter)(*this);}
			++iter;
		}
	}
	delete data;
}

//////////////////////////////////////////////////////////////////////////
// retrieve
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
NO_ObjectIter NO_Object::find		(NO_UInt32 r)			{return NO_Value::objectSet.find(r);}
NO_ObjectIter NO_Object::getEnd		(void)					{return NO_Value::objectSet.end();}
NO_ObjectIter NO_Object::getBegin	(void)					{return NO_Value::objectSet.begin();}
NO_ObjectIter NO_Object::getNext	(NO_ObjectIter& iter)	{return (++(iter.iter));}
	
//////////////////////////////////////////////////////////////////////////
// retrieve
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
NO_Boolean	NO_Object::getInitialized(void)	const {return data!=0;}

//////////////////////////////////////////////////////////////////////////
NO_UInt32 NO_Object::getNum(void) const
{
	
	if(!data){return 0;}
	return data->getNum();
}

//////////////////////////////////////////////////////////////////////////
NO_UInt32 NO_Object::getID(void)	const
{
	
	if(!data){return 0;}
	return (data->getID());
}

//////////////////////////////////////////////////////////////////////////
std::string	NO_Object::getName	(void)	const
{
	
	if(!data)		{return "unnamed";}
	if(!data->name)	{return "unnamed";}
	return *data->name;
}

//////////////////////////////////////////////////////////////////////////
const NO_Type& NO_Object::getType(void)	const
{
	
	if(!data){throw std::exception("Object empty");}
	return data->getType();
}

//////////////////////////////////////////////////////////////////////////
const NO_Type& NO_Object::getDynamicType(void) const
{
	
	if(!data){throw std::exception("Object empty");}
	return data->getDynamicType();
}

//////////////////////////////////////////////////////////////////////////
NO_Object NO_Object::getMaster(void) const
{
	
	if(!data){throw std::exception("Object empty");}
	if(!data->depend){return empty;}
	return data->depend;	
}

//////////////////////////////////////////////////////////////////////////
// use
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
NO_Boolean NO_Object::isKindOf(const NO_Type& t) const
{
	return (data->getDynamicType().findBase(t.getID())!=0);
}

//////////////////////////////////////////////////////////////////////////
NO_Object NO_Object::cast (const NO_Type& t) const 
{
	if(!data){throw std::exception("Object empty");}

	if(t==data->type){return data;}
	const NO_IBase* base = data->type.findBase(t.getID());
	if(!base){return data;}
	return base->getBase(*data);
}

//////////////////////////////////////////////////////////////////////////
NO_Object NO_Object::base(const NO_IBase& base)		const 
{
	
	if(!data){throw std::exception("Object empty");}
	return base.getBase(*data);
}

//////////////////////////////////////////////////////////////////////////
NO_Object NO_Object::member(const NO_IMember& member) const 
{
	
	if(!data){throw std::exception("Object empty");}
	return member.getMember(*data);
}

//////////////////////////////////////////////////////////////////////////
NO_Object NO_Object::element(NO_UInt32 index) const 
{
	
	if(!data){throw std::exception("Object empty");}
	return data->type.getElement(*data,index);
}

//////////////////////////////////////////////////////////////////////////
NO_Object NO_Object::reference (void) const 
{
	if(!data){throw std::exception("Object empty");}
	return data->type.getReference(*data);
}

//////////////////////////////////////////////////////////////////////////
NO_Object NO_Object::dereference	(void) const 
{
	if(!data){throw std::exception("Object empty");}
	return data->type.getDereference(*data);
}

//////////////////////////////////////////////////////////////////////////
NO_Object NO_Object::copy	(void) const 
{
	if(!data){throw std::exception("Object empty");}
	return data->type.getCopy(*data);
}

//////////////////////////////////////////////////////////////////////////
NO_Object NO_Object::base(const NO_Int8* name) const
{
	if(!data) {throw std::exception("Object empty");}
	
	const NO_IBase* b = data->getType().findBase(name);
	if(!b){throw std::exception("Invalid base access.");}
	
	return b->getBase(*data);
}

//////////////////////////////////////////////////////////////////////////
NO_Object NO_Object::member(const NO_Int8* name) const
{
	
	if(!data) {throw std::exception("Object empty");}

	const NO_IMember* m = data->getType().findMember(name);
	if(!m){throw std::exception("Invalid member access.");}
	
	return m->getMember(*data);
}

//////////////////////////////////////////////////////////////////////////
const NO_IFunction& NO_Object::function(NO_UInt32 id)
{
	if(!data) {throw std::exception("Object empty");}

	const NO_IFunction* m = data->getType().findFunction(id);
	if(!m){throw std::exception("Invalid function access");}
	
	return *m;
}

//////////////////////////////////////////////////////////////////////////
const NO_IFunction& NO_Object::function(const NO_Int8* name, NO_TypeList& list)
{
	if(!data) {throw std::exception("Object empty");}

	const NO_IFunction* m = data->getType().findFunction(name,list);
	if(!m){throw std::exception("Invalid function access");}
	
	return *m;
}

//////////////////////////////////////////////////////////////////////////
void NO_Object::call(NO_UInt32 id, NO_Object& r)
{
	const NO_IFunction& m = function(id);
	if(&r!=&empty){m(*this,NO_ObjectList::empty,r);}
	else{m(*this,NO_ObjectList::empty);}
}

//////////////////////////////////////////////////////////////////////////
void NO_Object::call(NO_UInt32 id, NO_ObjectList& p, NO_Object& r)
{
	const NO_IFunction& m = function(id);
	if(&r!=&empty){m(*this,p,r);}
	else{m(*this,p);}
}

//////////////////////////////////////////////////////////////////////////
void NO_Object::call(const NO_Int8* n, NO_Object& r)
{
	const NO_IFunction& m = function(n,NO_TypeList::empty);
	if(&r!=&empty){m(*this,NO_ObjectList::empty,r);}
	else{m(*this,NO_ObjectList::empty);}
}

//////////////////////////////////////////////////////////////////////////
void NO_Object::call(const NO_Int8* n, NO_ObjectList& p, NO_Object& r)
{
	NO_TypeList list;
	p.addTypes(list);
	const NO_IFunction& m = function(n,list);
	if(&r!=&empty){m(*this,p,r);}
	else{m(*this,p);}
}

//////////////////////////////////////////////////////////////////////////
NO_OArchive&	NO_Object::output (NO_OArchive& a)
{
	
	if(!data){throw std::exception("Object empty");}
	return data->type.output(*this,a);
}

//////////////////////////////////////////////////////////////////////////
NO_IArchive&	NO_Object::input  (NO_IArchive& a)
{
	
	if(!data){throw std::exception("Object empty");}
	return data->type.input(*this,a);
}

